package com.hulk.rpc.common;

import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.NettyRuntime;
import lombok.extern.slf4j.Slf4j;

/**
 * jvm实例共享EventLoopGroup
 */
@Slf4j
public class EventLoopGroupHolder {
  private static final int NIO_THREAD_COUNT = NettyRuntime.availableProcessors() * 2;

  private static int reference = 0;
  private static EventLoopGroup eventLoopGroup;

  static {
    if (Epoll.isAvailable()) {
      eventLoopGroup = new EpollEventLoopGroup(NIO_THREAD_COUNT);
      ((EpollEventLoopGroup) eventLoopGroup).setIoRatio(99);
    } else {
      eventLoopGroup = new NioEventLoopGroup(NIO_THREAD_COUNT);
      ((NioEventLoopGroup) eventLoopGroup).setIoRatio(99);
    }

    Runtime.getRuntime().addShutdownHook(new Thread(EventLoopGroupHolder::close, "eventLoopGroup-close-thread"));
  }

  /**
   * 获取EventLoopGroup， 使用完毕必须执行{@link #release(EventLoopGroup)}
   */
  public synchronized static EventLoopGroup get() {
    if (eventLoopGroup == null) {
      throw new RuntimeException("eventLoopGroup has been closed");
    }

    ++reference;
    return eventLoopGroup;
  }

  /**
   * 释放eventLoopGroup，内部使用引用计数，当计数为0时实际关闭
   */
  public synchronized static void release(EventLoopGroup eventLoopGroup) {
    if (--reference == 0) {
      close();
    }
  }

  private synchronized static void close() {
    if (eventLoopGroup == null) {
      return;
    }

    try {
      eventLoopGroup.shutdownGracefully().syncUninterruptibly();

      if (log.isInfoEnabled()) {
        log.info("成功关闭 eventLoopGroup");
      }
    } catch (Throwable t) {
      if (log.isWarnEnabled()) {
        log.warn("eventLoopGroup shutdown error", t);
      }
    }
    eventLoopGroup = null;
  }
}
